package lib;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;

public class Primes {
	
	private List<Integer> primes;
	private long sieveSize;
	private BitSet composites;
	private boolean sieved = false;
	
	public Primes(int initialSize) {
		this.primes = new ArrayList<Integer>();
		this.sieveSize = initialSize;
		this.composites = new BitSet();
		composites.set(0);
		composites.set(1);
		primes.add(2);
		primes.add(3);
		composites.set(4);
		primes.add(5);
		composites.set(6);
	}
	
	public Primes() {
		this(100000000);
	}
	
	private void increaseSieveSize() {
		this.sieveSize *= 2;
		System.out.println("sieve size is now " + this.sieveSize);
	}
	
	public long getPrime(int n) {
		if (!sieved) doSieve();
		while (primes.size() < n) {
			increaseSieveSize();
			doSieve();
		}
		
		return primes.get(n - 1);

	}
	
	public boolean isPrime(int i) {
		
		if (i <= 7) {
			switch((int)i) {
				case 2: return true;
				case 3: return true;
				case 5: return true;
				case 7: return true;
				default: return false;
			}
		} else if (	i % 2 == 0 ||
					i % 3 == 0 ||
					i % 5 == 0 ||
					i % 7 == 0
		) {
			return false;
		} else if (sieveSize >= i) {
			if (!sieved) doSieve();
			return !composites.get(i);
		} else {
			return MillerRabin32.isPrime(i);
		}
	}
	
	private synchronized void doSieve() {
		for (int prime = 2; prime < sieveSize; prime = composites.nextClearBit(prime+1)) {
			if (prime > getGreatestKnownPrime()) {
				primes.add(prime);
			}
			for (int multiplier = 2, multiple = multiplier * prime; multiple <= sieveSize; multiplier++, multiple = multiplier * prime) {
				composites.set(multiple);
			}
		}
		sieved = true;
	}
	
	private long getGreatestKnownPrime() {
		return primes.get(primes.size() - 1);
	}
}
